home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 December / PCWDEC07.iso / Software / Freeware / FlashGot 0.6.4 / chrome / flashgot.jar / content / flashgot / flashgotGalleryBuilder.js < prev    next >
Encoding:
JavaScript  |  2007-08-29  |  18.9 KB  |  660 lines

  1. /***** BEGIN LICENSE BLOCK *****
  2.  
  3.     FlashGot - a Firefox extension for external download managers integration
  4.     Copyright (C) 2004-2006 Giorgio Maone - g.maone@informaction.com
  5.  
  6. ***** END LICENSE BLOCK *****/
  7.  
  8. function FlashGotGalleryBuilder() {}
  9.  
  10. FlashGotGalleryBuilder.INTERVAL_RX=/\[\s*(\d+)\s*-\s*(\d+)\s*(;{0,1}\s*\d*)\s*\]/;
  11. FlashGotGalleryBuilder.INTERVAL_AZ_RX=/\[\s*([a-z]{1})\s*-\s*([a-z]{1})\s*(;{0,1}\s*\d*)\s*\]/i;
  12. FlashGotGalleryBuilder.EXPR_RX=/\[\s*([\w]+)\s*\((.*?)\)\s*\]/i;
  13.  
  14. FlashGotGalleryBuilder.prototype = {
  15.   
  16.   urlsTableModel: {
  17.     selection: null,
  18.     data: [],
  19.     get rowCount() { return this.data.length; },
  20.     getCellText: function(row, col) {
  21.       return this.data[row][col.id?col.id:col];
  22.     },
  23.     setTree: function(treeBox) { this.treeBox = treeBox; },
  24.     isContainer: function(index) { return false; },
  25.     isSeparator: function(index) { return false; }, 
  26.     isSorted: function() { return false; },
  27.     getLevel: function(index) { return 0; },
  28.     getImageSrc: function(row, col) {
  29.       return null;
  30.     },
  31.     getCellProperties: function(row, col, props) {},
  32.     getColumnProperties: function(column, elem, prop) {}, 
  33.     getRowProperties: function(row, props) { },
  34.   
  35.     isContainerOpen: function(index) { },
  36.     isContainerEmpty: function(index) { return false; },
  37.     canDropOn: function(index) { return false; },
  38.     canDropBeforeAfter: function(index, before) { return false; },
  39.     drop: function(row, orientation) { return false; },
  40.     
  41.     getParentIndex: function(index) { return 0; },
  42.     hasNextSibling: function(index, after) { return false; },
  43.     getProgressMode: function(row, column) { },
  44.     getCellValue: function(row, column) { },
  45.     toggleOpenState: function(index) { },
  46.     cycleHeader: function(col, elem) { },
  47.     selectionChanged: function() {
  48.       try {
  49.         gFlashGotGB.urlsPreviewDoc.getElementById(
  50.             FlashGotGalleryHTML.prototype.galleryId).innerHTML =
  51.             this.data[this.selection.currentIndex].html;
  52.       } catch(ex) {
  53.       } 
  54.     },
  55.     cycleCell: function(row, column) { },
  56.     isEditable: function(row, column) { return false; },
  57.     performAction: function(action) { },
  58.     performActionOnRow: function(action, row) { },
  59.     performActionOnCell: function(action, row, column) { }
  60.   },
  61.   
  62.   sandbox: Components.utils.Sandbox ? Components.utils.Sandbox("about:blank") : null,
  63.   expressions: {},
  64.   selectedExprName: null,
  65.   onload: function() {
  66.     try {
  67.       var data=window.arguments[0];
  68.       this.previewTextBox.value=data.previewURL;
  69.       this.contentTextBox.value=data.contentURL;
  70.       this.referrerTextBox.value=data.referrerURL;
  71.       this.originalWindow=data.originalWindow;
  72.       this.tmpDir=data.tmpDir;
  73.       this.prefs=data.prefs;
  74.       this.filePath=null;
  75.       if(this.sandbox) {
  76.         try {
  77.           this.expressions=Components.utils.evalInSandbox(this.prefs.getCharPref("buildGallery.expressions"), this.sandbox);
  78.         } catch(ex) {}
  79.         if(typeof(this.expressions)!="object" || !this.expressions) this.expressions = {};
  80.       } else {
  81.         document.getElementById("flashgotGB-expr-tab").setAttribute("disabled", "true");
  82.       }
  83.       this.normalizeURL(this.previewTextBox);
  84.       this.normalizeURL(this.contentTextBox);
  85.       
  86.       function fixColLabel(id) {
  87.         var col = document.getElementById(id+"Col");
  88.         col.setAttribute("label",col.getAttribute("label").replace(/:/g,""));
  89.       }
  90.       fixColLabel("preview");
  91.       fixColLabel("content");
  92.       this.urlsTable.view = this.urlsTableModel;
  93.       this.validateURLs();
  94.       document.getElementById("mainTabs").setAttribute("onselect","gFlashGotGB.tabSelected(event)");
  95.     } catch(e) {
  96.       gFlashGotService.log(e);
  97.       window.dump(e);
  98.       this.dialog.cancelDialog();
  99.     }
  100.     
  101.   }
  102. ,
  103.   saveExpressions: function() {
  104.     if(typeof(this.expressions)=="object" && this.expressions) {
  105.       this.prefs.setCharPref("buildGallery.expressions",this.expressions.toSource());
  106.     }
  107.   }
  108. ,
  109.   get dialog() {
  110.     return document.documentElement;
  111.   }
  112. ,
  113.   get previewBase() {
  114.     return this.trim(this.previewTextBox.value);
  115.   }
  116. , get contentBase() {
  117.     return this.trim(this.contentTextBox.value);
  118.   }
  119. , get referrer() {
  120.     return this.trim(this.referrerTextBox.value);
  121.   }
  122. ,
  123.   get previewTextBox() {
  124.     return document.getElementById("flashgotGB-preview-text");
  125.   }
  126. ,  
  127.   get contentTextBox() {
  128.     return document.getElementById("flashgotGB-content-text");
  129.   }
  130. ,
  131.   get referrerTextBox() {
  132.     return document.getElementById("flashgotGB-referrer-text");
  133.   }
  134.   get urlsTable() {
  135.     return document.getElementById("flashgotGB-urlsTable");
  136.   }
  137. ,
  138.   get urlsPreviewDoc() {
  139.     return document.getElementById("flashgotGB-urls-preview").contentDocument;
  140.   }
  141. ,
  142.   get exprListBox() { 
  143.     return document.getElementById("flashgotGB-expr-list");
  144.   }
  145. ,
  146.   get exprTextBox() {
  147.     return document.getElementById("flashgotGB-expr-text");
  148.   }
  149. ,
  150.   trim: function(s) {
  151.     return s.replace(/^\s+/g,"").replace(/\s+$/g,"");
  152.   }
  153.   checkIntervals: function(url) {
  154.     return url.search(FlashGotGalleryBuilder.INTERVAL_RX)>-1 || url.search(FlashGotGalleryBuilder.INTERVAL_AZ_RX)>-1;
  155.   }
  156. ,
  157.   normalizeURL: function(textBox) { 
  158.     var url=textBox.value;
  159.     var hasIntervals=this.checkIntervals(url);
  160.     if(!hasIntervals) {
  161.       url=url.replace(/(\{|\(|<)/g,"[").replace(/(\}|\)|>)/g,"]");
  162.       textBox.value=this.checkIntervals(url)
  163.         ?url
  164.         :textBox.value.replace(/(\d+)/g,"[$1-$1;1]");
  165.       }
  166.   }
  167. ,  
  168.   validateURLs: function() {
  169.  
  170.     var htmlBuilder=new FlashGotGalleryHTML(this);
  171.    
  172.     var valid = htmlBuilder.valid;
  173.     this.dialog.getButton("accept").setAttribute("disabled",!valid);
  174.     const model= this.urlsTableModel;
  175.     model.treeBox.rowCountChanged(0, -model.data.length);
  176.     const urlList = model.data = [];
  177.     
  178.      
  179.     if(valid) {
  180.       for(var html; html=htmlBuilder.nextFragment();) {
  181.         urlList[urlList.length] = {
  182.           html: html,
  183.           previewCol: htmlBuilder.currentPreviewURL,
  184.           contentCol: htmlBuilder.currentContentURL
  185.         }
  186.       }
  187.       model.treeBox.rowCountChanged(0, urlList.length);
  188.     }
  189.     
  190.     
  191.     const exprListBox=this.exprListBox;
  192.     const selectedExprName=this.selectedExprName;
  193.     exprListBox.setAttribute("suppressonselect","true");
  194.     while(exprListBox.getRowCount()>0) exprListBox.removeItemAt(0);
  195.     const exprNames=htmlBuilder.exprNames;
  196.     var selectedItem=null;
  197.     var len=exprNames.length;
  198.     for(var j=0; j<len; j++) {
  199.       item=exprListBox.appendItem(exprNames[j]);
  200.       if(selectedItem==null || item.label==selectedExprName) selectedItem=item;
  201.     }
  202.     
  203.     if(selectedItem) {
  204.       exprListBox.selectItem(selectedItem);
  205.     }
  206.     this.exprSelected();
  207.     
  208.     exprListBox.removeAttribute("suppressonselect");
  209.  
  210.   }
  211. ,
  212.   synchronizePreview: function() {
  213.     this.synchronizeIntervals(this.contentTextBox,this.previewTextBox,
  214.     { rx: /\.(mpg|mpeg|wmv|avi|mov)$/i, ext: ".jpg" });
  215.   }
  216. ,
  217.   synchronizeContent: function() {
  218.     this.synchronizeIntervals(this.previewTextBox,this.contentTextBox,
  219.     { rx: /\.(jpg|jpeg|gif|png|bmp)$/i, ext: ".mpg" });
  220.   }
  221. ,
  222.   synchronizeIntervals: function(srcBox,dstBox,extFix) {
  223.     var dst=this.trim(dstBox.value);
  224.     if(dst=="") {
  225.       dst=srcBox.value.replace(extFix.rx,extFix.ext);
  226.     } else {
  227.       var isrc=new FlashGotGalleryIterator(this.trim(srcBox.value));
  228.       var idst=new FlashGotGalleryIterator(dst);
  229.       dst="";
  230.       var src=""
  231.       while(isrc && idst && isrc.valid && idst.valid) {
  232.         dst=dst.concat(
  233.             idst.base.substring(0,idst.match.index)
  234.           ).concat(
  235.             isrc.match[0]
  236.           );
  237.          isrc=isrc.delegate;
  238.          idst=idst.delegate;
  239.       }
  240.     }
  241.     if(idst) dst=dst.concat(idst.base);
  242.     dstBox.value=dst;
  243.     this.validateURLs();
  244.   }
  245. ,
  246.   build: function() {
  247.     var htmlBuilder=new FlashGotGalleryHTML(this);
  248.   
  249.     const cc=Components.classes;
  250.     const ci=Components.interfaces;
  251.    
  252.     const galFile=cc["@mozilla.org/file/local;1"].createInstance(ci.nsILocalFile);
  253.     galFile.initWithPath(this.tmpDir.path);
  254.     galFile.append("flashgotGB.html");
  255.     galFile.createUnique(0,-1);
  256.     
  257.     this.filePath=galFile.path;
  258.     
  259.     const os=cc["@mozilla.org/network/file-output-stream;1"].createInstance(
  260.       ci.nsIFileOutputStream);
  261.     
  262.     try {
  263.       os.init(galFile,0x02,-1,0);
  264.       
  265.       var html=htmlBuilder.header; 
  266.       os.write(html,html.length);
  267.       
  268.       while(html=htmlBuilder.nextFragment() ) {
  269.         os.write(html,html.length);
  270.         if(!htmlBuilder.valid) break;
  271.       }
  272.       
  273.       html=htmlBuilder.footer;
  274.       os.write(html,html.length);
  275.     
  276.     } finally {
  277.       os.close();
  278.     }
  279.     const ios=cc['@mozilla.org/network/io-service;1'].getService(ci.nsIIOService);
  280.     
  281.     var w=this.originalWindow;
  282.     var url=ios.newFileURI(galFile).spec;
  283.     if(typeof(w.messenger)=="object" && w.messenger.OpenURL) { 
  284.       // Thunderbird
  285.       w.messenger.OpenURL(url);
  286.     } else if(w.closed) {
  287.       w = window.open(url,"_blank");
  288.     } else {
  289.       var browser = w.getBrowser();
  290.       // gFlashGotService.referrerSpoofer.connect(browser)
  291.       browser.selectedTab = browser.addTab(url, null);
  292.     }
  293.   }
  294. ,
  295.   createExpression: function(text) {
  296.     expr = { text: text };
  297.     try {
  298.       expr.func = Components.utils.evalInSandbox("_f = function() {\n" + text + "\n}", this.sandbox);
  299.       expr.err=null;
  300.     } catch(err) {
  301.       expr.err=err;
  302.     }
  303.     return expr;
  304.   }
  305. ,
  306.   func2Expr: function(func) {
  307.     var f = func.toString();
  308.     f=f.substring(f.indexOf('{')+1,f.lastIndexOf('}'));
  309.     var indent=f.match(/^([ \t]+)\w/m);
  310.     if(indent) {
  311.       var spaces=indent[1];
  312.       var rxSpaces=new RegExp(spaces,'g');
  313.       f=f.replace(new RegExp('^'+spaces+'((?:'+spaces+')*)','mg'),
  314.       function($0,$1) {
  315.         return $1.replace(rxSpaces,' ');
  316.       });
  317.     }
  318.     return f.replace(/^[\s]*\n/gm,'');
  319.   }
  320. ,
  321.   tabSelected: function(ev) {
  322.     switch(ev.target.selectedItem.id) {
  323.       case "flashgotGB-url-tab":
  324.         this.exprChanged();
  325.         this.validateURLs();
  326.         break;
  327.       case "flashgotGB-expr-tab":
  328.           this.validateURLs();
  329.           this.exprTextBox.focus();
  330.     }
  331.   }
  332. ,
  333.   exprSelected: function() {
  334.     const exprTextBox=this.exprTextBox;
  335.     const exprDes=document.getElementById("flashgotGB-expr-des");
  336.     const rxFxName=/\bfunction \w+\(/;
  337.     const errorTextBox=document.getElementById("flashgotGB-expr-error-text");
  338.     const selectedItem=this.exprListBox.selectedItem;
  339.     this.exprChanged();
  340.     if(!selectedItem) {
  341.       this.selectedExprName=null;
  342.       exprTextBox.value="";
  343.       exprDes.value=exprDes.value.replace(rxFxName,"function fx(");
  344.       exprTextBox.setAttribute("disabled",true);
  345.       errorTextBox.value="";
  346.     } else {
  347.       exprTextBox.removeAttribute("disabled");
  348.       const exprName=selectedItem.label;
  349.       this.selectedExprName=exprName;
  350.       const expr=this.expressions[exprName];
  351.       exprTextBox.value=expr?expr.text:'return "";';
  352.       exprDes.value=exprDes.value.replace(rxFxName,"function "+exprName+"(");
  353.       this.exprChanged();
  354.     }
  355.   }
  356.   exprChanged: function() {
  357.     const selectedExprName=this.selectedExprName;
  358.     if(!selectedExprName) {
  359.       this.exprTextBox.setAttribute("disabled",true);
  360.     } else {
  361.       this.exprTextBox.removeAttribute("disabled");
  362.       var text=this.exprTextBox.value;
  363.       var expr=this.expressions[selectedExprName];
  364.       if(text.replace(/\s+/,'').length) {
  365.         if( (!expr) || expr.text!=text) {
  366.           expr = this.expressions[selectedExprName] = this.createExpression(text);
  367.           this.saveExpressions();
  368.         }
  369.       } else if(expr) {
  370.         delete this.expressions[selectedExprName];
  371.         this.saveExpressions();
  372.       }
  373.     }
  374.     this._hilightErrors();
  375.   }
  376. ,
  377.   _hilightErrors: function() {
  378.     const exprList=this.exprListBox;
  379.     exprList.style.background="white";
  380.     const ee=this.expressions;
  381.     var item,expr;
  382.     for(var j=exprList.getRowCount(); j-->0;) {
  383.       item=exprList.getItemAtIndex(j);
  384.       expr=ee[item.label];
  385.       if(expr) {
  386.         item.style.color=expr.err?"red":"black";
  387.         if(item.selected) {
  388.           document.getElementById("flashgotGB-expr-error-text").value=expr.err;
  389.           this.exprTextBox.style.color=item.style.color;
  390.         }
  391.       }
  392.     }
  393.   }
  394. }
  395.  
  396.  
  397. function FlashGotGalleryHTML(builder) {
  398.   this.builder=builder;
  399.   this.previews=new FlashGotGalleryIterator(builder.previewBase);
  400.   this.contents=new FlashGotGalleryIterator(builder.contentBase);
  401.   var exprNames=[];
  402.   var name,expr;
  403.   
  404.   for(var base=builder.previewBase.concat(builder.contentBase), match=null;
  405.     match=base.match(FlashGotGalleryBuilder.EXPR_RX);
  406.     base=base.substring(match.index+match[0].length)
  407.     ) {
  408.      exprNames[exprNames.length]=match[1];
  409.   }
  410.   const ee=builder.expressions;
  411.   
  412.   for(name in ee) {
  413.     exprNames[exprNames.length]=name;
  414.     expr=ee[name];
  415.     this.env[name] = expr.func ? expr.func : function() { throw new Error("["+name+"()]"+" not implemented!"); };
  416.   }
  417.   
  418.   exprNames=exprNames.sort();
  419.   for(var prevName=null, j=exprNames.length; j-->0;) {
  420.     name=exprNames[j];
  421.     if(name==prevName) exprNames.splice(j,1);
  422.     else prevName=name;
  423.   }
  424.   this.exprNames=exprNames;
  425.   this.buildDOM(this.builder.urlsPreviewDoc);
  426. }
  427.  
  428. FlashGotGalleryHTML.prototype = {
  429.   index: 0,
  430.   env: Components.utils.Sandbox ? Components.utils.Sandbox("about:blank") : null,
  431.   galleryId: "flashgotGB-gallery"
  432. ,
  433.   get headElementSource() {
  434.     const persist={
  435.       referrer: this.builder.referrer,
  436.       previews: this.previews.base,
  437.       contents: this.contents.base
  438.     };
  439.     return '<head><title>'+this.builder.referrer+" - "
  440.       +this.builder.dialog.getAttribute('title')+'</title>\n'
  441.         +'<script type="text/javascript">'
  442.         + "_flashgotGB="+persist.toSource()
  443.         +'</script>\n'
  444.         +'<style type="text/css">\n'
  445.         +'body,div { font-family: verdana,arial,helvetica,sans-serif; '
  446.         +'font-size: 10px; color: black; background: white }\n'
  447.         +'a { color: blue; text-decoration: underline; }\n'
  448.         +'</style></head>'
  449.         ;
  450.   }
  451.   get header() {
  452.      return this.headElementSource+'<body><div id="'+this.galleryId+'">';
  453.   }
  454. ,  
  455.   get footer() {
  456.     return "\n</div></body>";
  457.   }
  458. ,
  459.   _eval: function(ctx, parm) {
  460.     return Components.utils.evalInSandbox("_func(" + parm + ")", ctx);
  461.   }
  462. ,
  463.   // evaluates macros and javascript functions
  464.   _macroPattern: /\[\$(0*\d)\]/g,
  465.   evalExpressions: function(iterator) {
  466.     var url=iterator.nextURL();
  467.     if(!url) return url;
  468.     const index=this.index;
  469.     
  470.     // macro evaluation
  471.     url=url.replace(this._macroPattern,function(all,digits) {
  472.       var n=parseInt(digits);
  473.       const len=digits.length;
  474.       var res;
  475.       if(n==0) {
  476.          res=index.toString();
  477.       } else {
  478.         var delegate=iterator;
  479.         while(n-->1 && delegate) delegate=delegate.delegate;
  480.         if(!delegate) return all;
  481.         res=delegate.renderedCursor;
  482.         if(len>0) res=res.toString().replace(/^[0]+/,"");
  483.       }
  484.       
  485.       return len>0?
  486.         digits.substring(0,digits.length-res.length).concat(res):res;
  487.     });
  488.     
  489.     if(!(this.exprNames.length && this.env))  return url;
  490.     
  491.     // function evaluation
  492.     var base = url;
  493.     var evalURL = "";
  494.     const ee=this.builder.expressions;
  495.     const ctx = this.env;
  496.     ctx.index = index;
  497.     ctx.baseURL = base;
  498.     
  499.     var name, expr, subst, res;
  500.     
  501.     for(var match=null;
  502.         match=base.match(FlashGotGalleryBuilder.EXPR_RX);
  503.         base=base.substring(match.index + match[0].length)
  504.     ) {
  505.       name=match[1];
  506.       expr=ee[name];
  507.       subst=match[0];
  508.       if(expr && expr.func) {
  509.         try {
  510.           ctx._func=expr.func;
  511.           res = this._eval(ctx, match[2]);
  512.           if(res != null && typeof(res) != "undefined") {
  513.             subst=res;
  514.           }
  515.           expr.err=null;
  516.         } catch(err) {
  517.           expr.err=err;
  518.         }
  519.       }
  520.       evalURL+=base.substring(0,match.index).concat(subst);
  521.     }
  522.     evalURL+=base;
  523.     return evalURL;
  524.   }
  525. ,
  526.   nextFragment: function() {
  527.     this.index++;
  528.     var p = this.currentPreviewURL = 
  529.     this.evalExpressions(this.previews);
  530.     var c = this.currentContentURL = 
  531.     this.evalExpressions(this.contents);
  532.     if( 
  533.        (! (p || c) )
  534.       || (p==null && !this.contents.valid) 
  535.       || (c==null && !this.previews.valid) 
  536.     ) {
  537.       return null;
  538.     }
  539.  
  540.     var html=p?'<img src="'+p+'" alt="'+(c?c:"???")+'" />':c+'<br />\n';
  541.     if(c) html='<a href="'+c+'">'+html+'</a>\n';
  542.     return html;
  543.   }
  544. ,  
  545.   get valid() {
  546.     return this.previews.valid || this.contents.valid;
  547.   }
  548. ,  
  549.   reset: function() {
  550.     this.previews.reset();
  551.     this.contents.reset();
  552.     this.index=0;
  553.   }
  554. ,
  555.   buildDOM: function(doc) {
  556.     if(!doc.getElementById(this.galleryId)) {
  557.       doc.documentElement.innerHTML=this.headElementSource;
  558.       doc.documentElement.appendChild(doc.createElement("body")
  559.       ).appendChild(doc.createElement("div")).id=this.galleryId;
  560.     }
  561.   }
  562.  
  563. }
  564.  
  565.  
  566. function FlashGotGalleryIterator(base) {
  567.   this.base=base;
  568.   var match=FlashGotGalleryBuilder.INTERVAL_RX.exec(this.base);
  569.   var matchAZ=FlashGotGalleryBuilder.INTERVAL_AZ_RX.exec(this.base);
  570.   if(match && ( (!matchAZ) || matchAZ.index>match.index) ) {
  571.     var idx=match.index;
  572.     this.isAZ=false;
  573.     this.start=parseInt(match[1],10);
  574.     this.end=parseInt(match[2],10);
  575.     this.padding="";
  576.     for(var j=(this.end>this.start?match[1]:match[2]).length; 
  577.       j-->0; 
  578.       this.padding=this.padding.concat("0") 
  579.       );
  580.     this.valid=true;
  581.   } else if(this.isAZ=((match=matchAZ)!=null)) {
  582.     if(/[a-z]{1}/.test(match[1])) {
  583.       match[2]=match[2].toLowerCase();
  584.     } else {
  585.       match[2]=match[2].toUpperCase();
  586.     }
  587.     this.start=match[1].charCodeAt(0);
  588.     this.end=match[2].charCodeAt(0);  
  589.     this.valid=true;
  590.   } else {
  591.     this.valid=false;
  592.     return;
  593.   }
  594.   
  595.   this.match=match;
  596.   
  597.   var stepMatch=this.match[3].match(/;\s*(\d+)/);
  598.   this.step =  (stepMatch?parseInt(stepMatch[1],10):1
  599.     ) * (this.start<=this.end?1:-1);
  600.     
  601.   this.cursor=this.start;
  602.  
  603.   this.delegate=new FlashGotGalleryIterator(
  604.     this.match.input.substring(this.match.index+this.match[0].length)
  605.     );
  606. }
  607.   
  608. FlashGotGalleryIterator.prototype = {
  609.   renderedCursor: "",
  610.   reset: function() {
  611.     this.cursor=this.start;
  612.     if(this.delegate) this.delegate.reset();
  613.   }
  614. ,
  615.   nextURL: function() {
  616.    
  617.     if(!this.valid) return this.base;
  618.     if(this.step==0
  619.       || (this.step>0 && this.cursor>this.end)
  620.       || (this.step<0 && this.cursor<this.end)) {
  621.       return null;
  622.     }
  623.     
  624.     var count;
  625.     
  626.     if(this.isAZ) {
  627.       count=String.fromCharCode(this.cursor);
  628.     } else {
  629.       count=new String(this.cursor);
  630.       if(count.length<this.padding.length) {
  631.         count=this.padding.substring(count.length).concat(count);
  632.       }
  633.     }
  634.     
  635.     var delegatePart=this.delegate.nextURL();
  636.     if(delegatePart==null || !this.delegate.valid) {
  637.       this.cursor+=this.step;
  638.       if(delegatePart==null) {
  639.         this.delegate.reset();
  640.         return this.nextURL();
  641.       }
  642.     }
  643.     
  644.     this.renderedCursor=count;
  645.     
  646.     return this.match.input.substring(0,this.match.index
  647.       ).concat(count
  648.       ).concat(delegatePart);  
  649.    
  650.   }
  651.   
  652. }
  653.  
  654. var gFlashGotGB=new FlashGotGalleryBuilder();
  655.  
  656.